import java.util.Scanner;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Laurens
 */
public class Encapsulate_Downcast_Refactor implements RefactoringMethod
{
    /*
     * Initialization and declaration of variables
     */
    FileEditor fileE = new FileEditor();
    int lengthMethod = 0;
    int startMethod = 0;
    String variableName = "";
    String methodName = "";
    String type = "";
    characterEditor editor = new characterEditor();
    ErrorMessages error = new ErrorMessages();
    /*
     * End of initialization and declaration of variables
     */
    
    /**
     * Constructor does nothing
     */
    public Encapsulate_Downcast_Refactor()
    {
        
    }

    /**
     * This does the actual renaming of a method refactoring
     * @param parameters
     * @return 
     */
    @Override
    public Vector<String> refactor(Vector<String> parameters)
    {
        /*
         * Creates same look and feel for GUI
         */
        try 
        {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } 
        catch (Exception ex) 
        {
            Logger.getLogger(Rename_Parameter_Refactor.class.getName()).log(Level.SEVERE, null, ex);
        } 
        Vector<String> result = new Vector<String>();
        boolean objectFound = false;
        if (parameters.isEmpty())
        {
            result.add(error.getErrorMessages("notFound"));
            return result;
        }
        /*
         * Parameters declaration
         */
        String path = parameters.get(0);
        String file = "";
        result.add(path);
        String className = "";
        String classN = "";
        StringTokenizer stClassName = new StringTokenizer(path, "/");
        while(stClassName.hasMoreTokens())
        {
            className = stClassName.nextToken();
        }
        StringTokenizer st = new StringTokenizer(className, ".");
        classN = st.nextToken();
        Vector<String> methodList = get(file, path, classN);
        String method = "";
        if(parameters.get(1) == null)
        {
            if(methodList.size() < 20)
            {
                Object[] options = new Object[methodList.size()];
                for(int i = 0; i < methodList.size(); i++)
                {
                    options[i] = methodList.get(i);
                }
                method = JOptionPane.showInputDialog(null, "Please select the method?", "Method Selection", JOptionPane.QUESTION_MESSAGE, null, options,"").toString();
            }
            else
            {
                method = JOptionPane.showInputDialog("Enter old method");
            }
        }
        try
        {
            /*
             * Reads the file needed to do the rename refactor
             */
            file = fileE.read(path);
            /*
             * Gets all the neccessary values needed for the refactoring
             */
            setStartMethod(file);
            setMethodLength(file);
            setVariableName(file);
            int r = 0;
            r = lineNumbersWhereMethodCalled(file);
            setType(file, r);
            objectFound = checkIfObjectFound(file);

        }
        catch(Exception e)
        {

        }
        if(objectFound)
        {
            result.add(encapsulateMethod(file));
        }
        else
        {
            result.add("Cant refactor, sorry bitch");
            return result;
        }
        return result;
    }

    private String encapsulateMethod(String file) 
    {
        Scanner scanner = new Scanner(file);
        Vector<String> result = new Vector<String>();
        int i = 1;
        int returnLine = lineNumberOfReturnFunction(file);
        int otherLine = lineNumbersWhereMethodCalled(file);
        while(scanner.hasNext())
        {
            String currentLine = scanner.nextLine();
            if(i == startMethod)
            {
                result.add(currentLine.replaceAll(" Object ", " "+type+" "));
            }
            else if(i == returnLine)
            {
                result.add("return "+ "(" + type + ")" + " " + variableName + ";");
            }    
            else if(i == otherLine)
            {
                result.add(dealWithTypeError(currentLine));
            }
            else
            {
                result.add(currentLine);
            }
            i++;
        }
        String output = "";
        for(int y = 0; y < result.size(); y++)
        {
            output+= result.get(y) + "\n";
        }
        return output;
    }

    /** 
     * Determines the startMethod
     * @param file 
     */
    public void setStartMethod(String file)
    {
        Scanner scanner = new Scanner(file);
        int temp = 0;
        int i = 1;
        while(scanner.hasNext())
        {
            String currentLine = scanner.nextLine();
            if(temp == 0)
            {
                if(currentLine.contains("public "))
                {
                    if(currentLine.contains(" Object "))
                    {
                        temp = i;
                    }
                }
            }
            i++;
        } 
        startMethod = temp;
    }
    
    /**
     * Gets the variable name of the called Object
     * @param file 
     */
    public void setVariableName(String file)
    {
        String temp = "";
        int i = 1;
        Scanner scanner = new Scanner(file);
        while(scanner.hasNext())
        {
            scanner.nextLine();
            if(i == startMethod)
            {
                for(i = startMethod; i < startMethod+lengthMethod; i++)
                {
                    String currentLine = scanner.nextLine();
                    if(currentLine.contains("Object"))
                    {
                        if(currentLine.contains("="))
                        {
                            StringTokenizer st = new StringTokenizer(currentLine, " ");
                            st.nextToken();
                            temp = st.nextToken();
                        }
                    }
                }
            }
            i++;
        }
        variableName = temp;
    }

    /** 
     * Determines the method length
     * @param file 
     */
    public void setMethodLength(String file)
    {
        Scanner scanner = new Scanner(file);
        int length = 0;
        int openBrackets = 0;
        int closedBrackets = 0;
        int i = 1;
        while(scanner.hasNext())
        {
            String currentLine = scanner.nextLine();
            if(i == startMethod)
            {
                setMethodName(currentLine);
                if(currentLine.contains("{"))
                {
                    openBrackets++;
                    length++;
                }
                else if(scanner.nextLine().contains("{"))
                {
                    openBrackets++;
                    length++;
                }
                while(openBrackets != closedBrackets)
                {
                    String current2 = scanner.nextLine();
                    if(current2.contains("{"))
                    {
                        openBrackets++;
                    }
                    else if(current2.contains("}"))
                    {
                        closedBrackets++;
                    }
                    length++;
                }        
            }
            i++;
        } 
         lengthMethod = length;
    }
    
    /**
     * Sets the name of the Object
     * @param currentLine 
     */
    public void setMethodName(String currentLine)
    {
        StringTokenizer st = new StringTokenizer(currentLine);
        String temp = "";
        while(st.hasMoreTokens())
        {
            if(!st.equals("public"))
            {
                if(!st.equals("abstract"))
                {
                    if(!st.equals("Object"))
                    {
                        temp = st.nextToken();
                    }
                }
            }
        }
        StringTokenizer removeBrackets = new StringTokenizer(temp, "(");
        methodName = removeBrackets.nextToken();
    }
    
    /**
     * Returns the lineNumbers where methodName is called
     * @param file
     * @return 
     */
    public int lineNumbersWhereMethodCalled(String file)
    {
        Scanner scanner = new Scanner(file);
        int result = 0;
        int i = 1;
        while(scanner.hasNext())
        {
            String currentLine = scanner.nextLine();
            if(i == startMethod)
            {
                for(i = startMethod; i < startMethod+lengthMethod; i++)
                {
                    
                }
            }
            else
            {
                if(currentLine.contains(methodName+"("))
                {
                    result = i;
                }
            }
            i++;
        }
        return result;
    }
    
    /**
     * Sets the type variable to the new type
     * @param file
     * @param lineNumbers 
     */
    public void setType(String file, int lineNumber)
    {
        Scanner scanner = new Scanner(file);
        String temp = "";
        int i = 1;
        int z = 0;
        while(scanner.hasNext())
        {
            String currentLine = scanner.nextLine();
            if(i == lineNumber)
            {
                if(currentLine.contains(") "+methodName))
                {
                    temp = getType(currentLine);
                }
                else if(currentLine.contains(")"+methodName))
                {
                    temp = getType(currentLine);
                }
                z++;
            }
            i++;
        }
        type = temp;
    }

    /**
     * Gets the new return type
     * @param currentLine
     * @return 
     */
    private String getType(String currentLine) 
    {
        if("".equals(type))
        {
            StringTokenizer st = new StringTokenizer(currentLine, "(");
            String temp = "";
            temp = st.nextToken();
            StringTokenizer st2 = new StringTokenizer(temp);
            return st2.nextToken();
        }
        else
        {
            return type;
        }
    }
    
    /**
     * Gets the lineNumber of the return statement inside the function scope
     * @param file
     * @return 
     */
    public int lineNumberOfReturnFunction(String file)
    {
        Scanner scanner = new Scanner(file);
        int result = 0;
        int i = 1;
        while(scanner.hasNext())
        {
            scanner.nextLine();
            if(i == startMethod)
            {
                for(i = startMethod; i < startMethod+lengthMethod; i++)
                {
                    String currentLine = scanner.nextLine();
                    if(currentLine.contains("return "))
                    {
                        result = i+1;
                        return result;
                    }
                }
            }
            i++;
        }
        return result;
    }

    /**
     * Removes casting function
     * @param currentLine
     * @return 
     */
    private String dealWithTypeError(String currentLine) 
    {
        StringTokenizer st = new StringTokenizer(currentLine);
        String result = "";
        while(st.hasMoreTokens())
        {
            String current = st.nextToken();
            if(current.equals(type))
            {
                result += current+" ";
            }
            else if(current.equals("("+type+")"))
            {
                result += "";
            }
            else if(current.contains("("+type+")"))
            {
                String temp = "";
                temp = current.replaceAll("("+type+")", "");
                result += temp.substring(2, temp.length());
            }
            else
            {
                result += current+" ";
            }
        }
        return result;
    }

    private boolean checkIfObjectFound(String file) 
    {
        Scanner scanner = new Scanner(file);
        boolean result = false;
        while(scanner.hasNext())
        {
            String currentLine = scanner.nextLine();
            if(currentLine.contains(" Object "))
            {
                result = true;
                return result;
            }
        }
        return result;
    }

    /**
     * Returns a description of the current refactoring method
     * @return 
     */
    @Override
    public String description() 
    {
        return "Removes casting of a function to inside the function decalaration.";
    }
    
    public Vector<String> get(String file, String path, String classN)
    {
        Scanner scanner =  new Scanner(file);
        Vector<String> result = new Vector<String>();
        Database db = new Database();
        Vector<String> methodList = new Vector<String>();
        try
        {
            db.connect();
            db.initializeDB();
            ParserProxy parser = new ParserProxy("FileParser");
            parser.parseString(path);
            methodList = db.getMethod(classN);
            //parameterList = db.getParamater(finalClassName);
        }
        catch(Exception e)
        {
            
        }
        while(scanner.hasNext())
        {
            String currentLine = scanner.nextLine();
            if(currentLine.contains("return "))
            {
                for(int u = 0; u < methodList.size(); u++)
                {
                    if(currentLine.contains("("))
                    {
                        if(currentLine.contains(methodList.get(u)+"("))
                        {
                            result.add(methodList.get(u));
                        }
                    }
                }
            }
        }
        return result;
    }
}
